package com.orcwar.gui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import android.app.Activity;
import android.util.Log;
import android.view.View;

import com.orcwar.controller.OWController;
import com.orcwar.engine.IOWGameListener;
import com.orcwar.engine.OWMap;
import com.orcwar.engine.OWPlayer;
import com.orcwar.engine.OWTile;
import com.orcwar.engine.OWUnit;
import com.orcwar.engine.level.OWLevelBuilder.OWLevelParameters;
import com.orcwar.engine.tile.OWAttackTile;
import com.orcwar.enums.DirectionEnum;
import com.orcwar.gui.animations.OWAttackAnimator;
import com.orcwar.gui.animations.OWUnitMover;

/**
 * OH GUY!</br> This class represents the GUI, it gethers inputs from android
 * activities and views, and calls controller methods.
 * 
 * @author baaleze
 * 
 */
public class OWGUI implements IOWGameListener{
	
	/** The game's canvas view. */
	private View gameView;
	
	/** The list of unitMovers which handle the smooth movement of units. */
	private List<OWUnitMover> unitMovers = new ArrayList<OWUnitMover>();
	
	/** The list of attack animators. */
	private List<OWAttackAnimator> attackAnimators = new ArrayList<OWAttackAnimator>();

	/** The list of tiles to highlight to show where a unit can move. */
	private Set<OWTile> tilesToHighlighMove = new HashSet<OWTile>();

	/** The list of tiles to highlight to show where a unit can move. */
	private Set<OWAttackTile> tilesToHighlighAttacks = new HashSet<OWAttackTile>();

	/** The list of tiles containing crosses displaying the unit path. */
	private Map<OWTile, DirectionEnum> tilesToDisplayPath = new HashMap<OWTile, DirectionEnum>();

	/**
	 * The tile of the destination of the move (the last tile with a red X)
	 * where the unit will move if the player touch it twice.
	 */
	private int destinationTileId;
	
	/** The tile the user is touching to attack. */
	private List<Integer> onShowPressedAttackTileIds = new ArrayList<Integer>();
	
	/** Whether the player is in attack mode. */
	private boolean attackModeEnabled;
	
	/** Whether the ui is blocked or not. */
	private boolean actionsBlocked;

	private Activity activity;

	private OWController controller;
	

	public OWGUI(Activity initialActivity, OWController controller) {
		this.activity = initialActivity;
		this.controller = controller;
	}

	public void highlightTiles(Set<OWTile> findPossibleTilesToMove,
			Set<OWAttackTile> findPossibleTilesToAttack) {
		if (findPossibleTilesToMove == null) {
			tilesToHighlighMove.clear();
		} else {
			tilesToHighlighMove = findPossibleTilesToMove;
		}
		
		if (findPossibleTilesToAttack == null) {
			tilesToHighlighAttacks.clear();
		} else {
			tilesToHighlighAttacks = findPossibleTilesToAttack;
		}
		
	}
	
	/**
	 * Clear all lists to clean all the gui.
	 */
	public void clearHighlights() {
		tilesToHighlighMove.clear();
		tilesToHighlighAttacks.clear();
		tilesToDisplayPath.clear();
		onShowPressedAttackTileIds.clear();
		destinationTileId = -1;
		attackModeEnabled = false;
	}

	public void displayPath(Map<OWTile, DirectionEnum> map) {
		if (map == null) {
			tilesToDisplayPath.clear();
		} else {
			tilesToDisplayPath = map;
		}
	}


	public void onUp(int x, int y) {
		controller.onUp(x, y);
	}

	public void handleTap(int x, int y) {
		controller.handleTap(x, y);
	}
	
	public boolean onShowPressed(int x, int y) {
		return controller.onShowPressed(x, y);
	}

	public void pass() {
		controller.pass();
	}

	public OWLevelParameters getCurrentGameInfo() {
		return controller.getCurrentGameInfo();
	}
	
	/**
	 * Handle the tap on the attack button.
	 */
	public void handleAttackButtonTap() {
		if (attackModeEnabled) {
			attackModeEnabled = false;
			return;
		}

		if (controller.getSelectedUnit() != null && !controller.getSelectedUnit().isTurnEnded()) {
			attackModeEnabled = true;
			tilesToDisplayPath.clear();
		}
	}

	
	/**
	 * Update the game for the current frame.
	 */
	public void update(long gameDuration) {
		// Update the unit movers
		updateMovers(gameDuration);
		
		// Update the attack anims
		updateAnimators(gameDuration);
	}
	
	
	private void updateMovers(long gameDuration) {
		Iterator<OWUnitMover> it = unitMovers.iterator();
		List<OWUnitMover> doneMovers = new ArrayList<OWUnitMover>();
		while (it.hasNext()) {
			OWUnitMover mover = it.next();
			if (mover.isDone()) {
				Log.d("Move", "Removed a unitMover");
				doneMovers.add(mover);
			} else {
				mover.update(gameDuration);
			}
		}
		for (OWUnitMover mover : doneMovers) {
			unitMovers.remove(mover);
			controller.onMoveOver(mover.getUnit());
		}
	}
	
	private void updateAnimators(long gameDuration) {
		Iterator<OWAttackAnimator> it2 = attackAnimators.iterator();
		List<OWAttackAnimator> doneAnimators = new ArrayList<OWAttackAnimator>();
		while (it2.hasNext()) {
			OWAttackAnimator mover = it2.next();
			if (mover.isDone()) {
				Log.d("Attack", "Removed an attackAnimator");
				doneAnimators.add(mover);
			} else {
				mover.update(gameDuration);
			}
		}
		for (OWAttackAnimator animator : doneAnimators) {
			attackAnimators.remove(animator);
			controller.onAttackOver(animator.getUnit(), animator.getAttackedUnits(), animator.isSuccess());
		}
	}
	
	//**********************//
	// ENGINE NOTIFICATIONS //
	
	@Override
	public void onUnitMove(OWUnit unit, OWTile startTile, OWTile endTile,
			List<OWTile> path) {
		 Log.d("Move", "Added a unitMover");
		 if (path != null && path.size() > 0) {
			 OWUnitMover mover = new OWUnitMover(this, unit, unit.getTile(),
			 path);
			 unitMovers.add(mover);
		 } else {
			 //Failed to move, display something on screen
			 //TODO:
		 }
	}
	
	@Override
	public void onUnitAttack(OWUnit unit, OWTile attackedTile, boolean success, List<OWUnit> attackedUnits) {
		OWAttackAnimator attackAnim = new OWAttackAnimator(this, unit, attackedTile, attackedUnits, success);
		attackAnimators.add(attackAnim);
		
		if (success) {
			//Do something
			Log.d("Attack", "Added an attack animator");
			
			if (attackedUnits != null) {
				//TODO:
			} else {
				//TODO:
			}
		} else {
			//Do something else
			//TODO:
		}
		
		if (unit.getAttackPoints() <= 0) {
			clearHighlights();
		}
	}
	
	@Override
	public void onLevelStart(OWMap map) {
		// TODO Auto-generated method stub
	}
	
	@Override
	public void onLevelEnd(OWMap game, OWPlayer player)
	{
		//TODO:
	}

	@Override
	public Runnable onTurnStart(OWPlayer currentPlayer, int turn)
	{
		if(getController().getPlayer().equals(currentPlayer))
		{
			//TODO:
		}
		else
		{
			//TODO:
		}
		return null;
	}

	@Override
	public void onTurnEnd(OWPlayer currentPlayer) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onUnitDeath(OWUnit unit) {
		// TODO Auto-generated method stub
	}
	
	@Override
	public void onUnitTurnEnd(OWUnit unit) {
		// TODO Auto-generated method stub	
	}
	
	// ENGINE NOTIFICATIONS //
	//**********************//
	
	//*******************//
	// GETTERS / SETTERS //
	
	
	public Set<OWTile> getTilesToHighlighMove() {
		return tilesToHighlighMove;
	}

	public void setTilesToHighlighMove(Set<OWTile> tilesToHighlighMove) {
		this.tilesToHighlighMove = tilesToHighlighMove;
	}

	public Set<OWAttackTile> getTilesToHighlighAttacks() {
		return tilesToHighlighAttacks;
	}
	
	/**
	 * Scan the tilesToHightlightAttacks to see whether the given tile is in
	 * it. If so, return the attackTile corresponding. Returns null otherwise.
	 * @param tile the tile to test
	 * @return a OWAttackTile if tile was in the list, null otherwise
	 */
	public OWAttackTile getTileToHighlighAttack(OWTile tile) {
		for(OWAttackTile attackTile : tilesToHighlighAttacks) {
			if (attackTile.getId() == tile.getId()) {
				return attackTile;
			}
		}
		return null;
	}

	public void setTilesToHighlighAttacks(Set<OWAttackTile> tilesToHighlighAttacks) {
		this.tilesToHighlighAttacks = tilesToHighlighAttacks;
	}

	public int getDestinationTileId() {
		return destinationTileId;
	}

	public void setDestinationTileId(int destinationTileId) {
		this.destinationTileId = destinationTileId;
	}

	public Map<OWTile, DirectionEnum> getTilesToDisplayPath() {
		return tilesToDisplayPath;
	}

	public void setTilesToDisplayPath(
			Map<OWTile, DirectionEnum> tilesToDisplayPath) {
		this.tilesToDisplayPath = tilesToDisplayPath;
	}

	public OWController getController() {
		return controller;
	}

	public void setController(OWController controller) {
		this.controller = controller;
	}

	/**
	 * @return the attackModeEnabled
	 */
	public boolean isAttackModeEnabled() {
		return attackModeEnabled;
	}

	/**
	 * @param attackModeEnabled the attackModeEnabled to set
	 */
	public void setAttackModeEnabled(boolean attackModeEnabled) {
		this.attackModeEnabled = attackModeEnabled;
	}

	/**
	 * @return the actionsBlocked
	 */
	public boolean isActionsBlocked() {
		return actionsBlocked;
	}

	/**
	 * @param actionsBlocked the actionsBlocked to set
	 */
	public void setActionsBlocked(boolean actionsBlocked) {
		this.actionsBlocked = actionsBlocked;
	}

	/**
	 * @return the gameView
	 */
	public View getGameView() {
		return gameView;
	}

	/**
	 * @param gameView the gameView to set
	 */
	public void setGameView(View gameView) {
		this.gameView = gameView;
	}

	public Activity getActivity() {
		return activity;
	}

	public void setActivity(Activity activity) {
		this.activity = activity;
	}

	/**
	 * @return the onShowPressedAttackTileIds
	 */
	public List<Integer> getOnShowPressedAttackTileIds() {
		return onShowPressedAttackTileIds;
	}

	/**
	 * @param onShowPressedAttackTileIds the onShowPressedAttackTileIds to set
	 */
	public void setOnShowPressedAttackTileIds(
			List<Integer> onShowPressedAttackTileIds) {
		this.onShowPressedAttackTileIds = onShowPressedAttackTileIds;
	}

	/**
	 * @return the attackAnimaters
	 */
	public List<OWAttackAnimator> getAttackAnimaters() {
		return attackAnimators;
	}

	/**
	 * @param attackAnimaters the attackAnimaters to set
	 */
	public void setAttackAnimaters(List<OWAttackAnimator> attackAnimaters) {
		this.attackAnimators = attackAnimaters;
	}
	
	
}
